package com.airlines.crewassignment.domain.util;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTime;

import com.airlines.common.ListSortUtil;
import com.airlines.common.SpringContextUtil;
import com.airlines.crewassignment.constant.LinkLineConstant;
import com.airlines.crewassignment.domain.Crew;
import com.airlines.crewassignment.domain.CrewLinkLineRoster;
import com.airlines.crewassignment.domain.CrewPositionAssignment;
import com.airlines.crewassignment.domain.DateTimeRange;
import com.airlines.crewassignment.domain.FlightInfo;
import com.airlines.crewassignment.domain.LinkLine;
import com.airlines.crewassignment.entity.ValueForDate;
import com.airlines.crewassignment.entity.linkline.TaskLoopFlightFact;
import com.airlines.crewassignment.service.linkline.TaskLoopFlightFactService;

/**
 * @author xxxx
 * @date 2018年4月16日 
 * @description
 */
public class DutyDurationUtil {
	private static TaskLoopFlightFactService taskLoopFlightFactService;
	private static TaskLoopFlightFactService getTaskLoopFlightFactService(){
		if(taskLoopFlightFactService == null){
			taskLoopFlightFactService = (TaskLoopFlightFactService)SpringContextUtil.getBean(TaskLoopFlightFactService.class);
		}
		return taskLoopFlightFactService;
	}

	/**
	 * 获取一个飞行员任意月的最大执勤时间
	 * 任意月，一般是排班日期开始当月，如果排班日期跨月，则包括下一月，以及未来任务环所在月的段
	 * 
	 * @return
	 */
	public static int getMaxDutyDurationEveryMonth(List<CrewPositionAssignment> list,Crew crew){
		int dutyDurationLastMonth = crew.getCrewFlightExperience().getDutyDurationLastMonth();
		List<ValueForDate> valueForDateListAll =  getMaxDutyDurationEveryDateRange(list,crew,dutyDurationLastMonth,"month");
		//按月分组合计
		List<ValueForDate> groupByMonth = ValueForDateUtil.groupByMonth(valueForDateListAll);
		return ValueForDateUtil.getMaxValue(groupByMonth);
	}
	
	
	/**
	 * 获取一个飞行员任意年的最大执勤时间
	 * 任意月，一般是排班日期开始当年，如果排班日期跨年，则包括下一年，以及未来任务环所在年的段
	 * @param list
	 * @param crew
	 * @return
	 */
	public static int getMaxDutyDurationEveryYear(List<CrewPositionAssignment> list,Crew crew){
		int dutyDurationLastYear = crew.getCrewFlightExperience().getDutyDurationLastYear();
		List<ValueForDate> valueForDateListAll =  getMaxDutyDurationEveryDateRange(list,crew,dutyDurationLastYear,"year");
		//按年分组合计
		List<ValueForDate> groupByYear = ValueForDateUtil.groupByYear(valueForDateListAll);
		return ValueForDateUtil.getMaxValue(groupByYear);
	}

	/**
	 * 获取一个飞行员任意七天的最大执勤时间
	 * @param list
	 * @param crew
	 * @return
	 */
	public static int getMaxDutyDurationEveryWeek(List<CrewPositionAssignment> list,Crew crew,CrewLinkLineRoster crewLinkLineRoster){

	    //1.获取排班开始结束时间
	    DateTimeRange assignmentDate = crewLinkLineRoster.getCrewLinkLineRosterGlobalValue().getAssignmentDateRange();
	    DateTime start = new DateTime(assignmentDate.getStart());
	    DateTime end = new DateTime(assignmentDate.getEnd());
	    //2.获取当前飞行员排班域中所有连线
		List<LinkLine> linkLineList = new ArrayList<>(CrewPositionAssignmentUtil.getLinkLineSet(list));
		int day  = 6 ;
		int max = 0 ;
		for(DateTime temp = start; !temp.isAfter(end) ;temp = temp.plusDays(1)){
		    //查询排班日期前几天的执勤期 + 排班域中几天的执勤期 = 总共七天的执勤期
			int beforeTime = day <= 0 ? 0 : getBeforeDutyDuration(crew,day);
			int nowTime = getNowDutyDuration(linkLineList,start,7-day);
			max = max > beforeTime+nowTime ? max : beforeTime+nowTime;
			day--;
		}
		return max;
	}

    /**
     * 获取当前排班中从开始日期到开始日期 + day 天数的执勤期
     * @param linkLineList
     * @param beginTime
     * @param day
     * @return
     */
    private static int getNowDutyDuration(List<LinkLine> linkLineList,DateTime beginTime, int day) {
        Map<Date,List<LinkLine>> dateLinkLineMap = linkLineGroupByFlightDate(linkLineList);
        int total = 0 ;
        for(int i = 0 ; i < day ; i ++){
        	List<LinkLine> tempList = dateLinkLineMap.get(beginTime.plusDays(i));
        	if(!CollectionUtils.isEmpty(tempList)){
        		 total += flightDutyDurationSum(tempList);
        	}
           
        }

        return total;
    }

    /**
	 * 获取排班日期之前几天的执勤器
	 * @param crew
	 * @param day 指定前几天
	 * @return
	 */
	private static int getBeforeDutyDuration(Crew crew,int day) {
        //肯定有6条数据
		List<ValueForDate> dutyDurations =  crew.getCrewFlightExperience().getDutyDurationList();
		int total = 0 ;
		for(int i = 0 ; i < day ; i++){
		    total += dutyDurations.get(5-i).getValue();
        }

        return total;

	}


	/**
	 * 将每天的值组装到每日实体类中
	 * @param list
	 * @param crew
	 * @param dutyDurationLast
	 * @param yearOrMonth
	 * @return
	 */
	private static List<ValueForDate> getMaxDutyDurationEveryDateRange(List<CrewPositionAssignment> list,Crew crew,int dutyDurationLast,String yearOrMonth){
		List<ValueForDate> valueForDateList = new ArrayList<>();
		//以前的执勤时间，算作排班第一天的
		valueForDateList.add(new ValueForDate(crew.getCrewFlightExperience().getBeforeFlightDate(),dutyDurationLast));
		
		if(CollectionUtils.isEmpty(list)){
			return valueForDateList;
		}

		//当前排班任务的
		List<LinkLine> linkLineList = new ArrayList<>(CrewPositionAssignmentUtil.getLinkLineSet(list));
		Map<Date,List<LinkLine>> dateLinkLineMap = linkLineGroupByFlightDate(linkLineList);
		for(Map.Entry<Date,List<LinkLine>> entry: dateLinkLineMap.entrySet()) {
			ValueForDate plan = new ValueForDate(entry.getKey(),flightDutyDurationSum(entry.getValue()));
			valueForDateList.add(plan);
		}

		//未来可能的的，算作排班最后一天的
		ListSortUtil.sort(linkLineList, true, "ta");
		
		//LinkLineLoopService linkLineLoopService  = getLinkLineLoopService();
		LinkLine linkLineEnd  = linkLineList.get(linkLineList.size()-1);
		DateTime flightDateTimeEnd = new DateTime(linkLineEnd.getFlightDate());
		DateTime end = null;
		if(StringUtils.equals("month", yearOrMonth)){
			end = flightDateTimeEnd.dayOfMonth().withMaximumValue();
		}else if(StringUtils.equals("year", yearOrMonth)){
			end = flightDateTimeEnd.dayOfYear().withMaximumValue();
		}


		//int dutyDurationFuture  = linkLineLoopService.getRemainDutyDurationOfLoopPlan(linkLineEnd.getLinkLineLoopFact(), end.toDate());
		TaskLoopFlightFactService taskLoopFlightFactService = getTaskLoopFlightFactService();
		int dutyDurationFuture =taskLoopFlightFactService.getRemainDutyDurationOfLoopPlan(linkLineEnd, end.toDate());
		ValueForDate future = new ValueForDate(linkLineEnd.getFlightDate(),dutyDurationFuture);
		valueForDateList.add(future);
		
		return valueForDateList;
	}

	/**
	 * 根据指定的一堆连线查询总共的执勤时间
	 * @param linkLines
	 * @return
	 */
	public static int  flightDutyDurationSum(List<LinkLine> linkLines){
		//1.按照日期分组
		Map<Date,List<LinkLine>> dateLinkLineMap = linkLineGroupByFlightDate(linkLines);

		//2.计算执勤时间  eg:（兰银宁青TA-青兰TD）+（20+90）-过站休息时间
		int dutyDurationSum = 0 ;
		for(Map.Entry<Date,List<LinkLine>> entry: dateLinkLineMap.entrySet()) {
			List<LinkLine> tempList = dateLinkLineMap.get(entry.getKey());
			dutyDurationSum += getDutyDurationByLinkLine(tempList);
		}

		return dutyDurationSum;
	}

	/**
	 * 连线按日期进行分组
	 * @param linkLines
	 * @return
	 */
	public static Map<Date,List<LinkLine>> linkLineGroupByFlightDate(List<LinkLine> linkLines){
		Map<Date,List<LinkLine>> dateLinkLineMap = new HashMap<>();
		for(LinkLine  l : linkLines){
			List<LinkLine> tempList = dateLinkLineMap.get(l.getFlightDate());
			if(tempList == null){
				tempList = new ArrayList<>();
				dateLinkLineMap.put(l.getFlightDate(),tempList);
			}
			tempList.add(l);
		}
		return dateLinkLineMap;
	}

	/**
	 * 查询某一个人 一天的执勤期
	 * @param linkLines 某一个人一天的所有连线
	 * @return
	 */
//	public static int getDutyDurationByLinkLine(List<LinkLine> linkLines){
//
//		if(linkLines == null || linkLines.size() == 0){
//			return 0;
//		}
//
//		Set<LinkLineLoopDay> loopDaySet = new HashSet<>();
//		DateTime maxDate = null;
//		DateTime minDate = null;
//		for(LinkLine l : linkLines){
//			DateTime ta =  new DateTime(l.getTa());
//			DateTime td =  new DateTime(l.getTd());
//			if(maxDate == null){
//				maxDate = ta;
//				minDate = td;
//			}else{
//				maxDate = maxDate.isAfter(ta) ? maxDate : ta;
//				minDate = minDate.isBefore(td) ? minDate : td;
//			}
//
//            LinkLineLoop linkLineLoopFact = l.getLinkLineLoopFact();
//            if(linkLineLoopFact != null){
//                loopDaySet.add(linkLineLoopFact.getLoopDayFact());
//            }
//		}
//
//		int totalPassTime = 0 ;
//        for(LinkLineLoopDay loopDay : loopDaySet){
//            totalPassTime += loopDay.getAirportRestDuration();
//        }
//
//		return Minutes.minutesBetween(minDate,maxDate).getMinutes() + 110 - totalPassTime;
//	}
	
	/**
	 * 查询某一个人 一天的执勤期
	 * 计算执勤期的核心计算公式！
	 * 规则：
	 * 1.获取所有连线的第一个航班STD作为开始时间，如果没有STD，则取第一个连线的TD  -->StartTime
	 * 2.获取最后一个航班的innTimeAcars滑入时间作为结束时间，如果没有滑入，则取innTimeQar，如果没有innTimeQar，则取sta，作为结束时间   -->EndTime
	 * 历史飞行执勤期(B)=最后一个航段的滑入-报到时间（第一个航段的计飞-90分钟）-过站休息时间
	 * 所有飞行执勤期（包括未来） EndTime - StartTime + 90分钟 - 所有航班的落地过站休息时间
	 * 
	 * @param linkLines 某一个人一天的所有连线，linkLines必须是正序排序，linkLines.flightInfoList 也必须是正序排序的
	 * @return
	 */
	public static int getDutyDurationByLinkLine(List<LinkLine> linkLines){
		if(CollectionUtils.isEmpty(linkLines)){
			return 0;
		}
		LinkLine fristLinkLine = linkLines.get(0);
		LinkLine lastLinkLine = linkLines.get(linkLines.size()-1);
		
		Date startTime = fristLinkLine.getTd();
		if(CollectionUtils.isNotEmpty(fristLinkLine.getFlightInfoList())){
			startTime = fristLinkLine.getFlightInfoList().get(0).getStd();
		}
		
		Date endTime = lastLinkLine.getTa();
		if(CollectionUtils.isNotEmpty(lastLinkLine.getFlightInfoList())){
			FlightInfo lastFlightInfo = lastLinkLine.getFlightInfoList().get( lastLinkLine.getFlightInfoList().size()-1);
			if(lastFlightInfo!=null){
				if(lastFlightInfo.getInnTimeAcars() != null){
					endTime = lastFlightInfo.getInnTimeAcars() ;
				}else if(lastFlightInfo.getInnTimeQar()!=null){
					endTime = lastFlightInfo.getInnTimeQar();
				}else if(lastFlightInfo.getSta() != null){
					endTime = lastFlightInfo.getSta();
				}
			}
					
		}
		
		//过站休息期计算
		int arrivalAirportRestTimeSum = 0;
		for(LinkLine l : linkLines) {
			for(FlightInfo f : l.getFlightInfoList()){
				for(TaskLoopFlightFact t : f.getTaskLoopFlightFactList()){
					if(StringUtils.equals(t.getModuleFlag(), l.getDutyCode())){
						if(t.getArrivalAirportRestTime() !=null){
							arrivalAirportRestTimeSum += t.getArrivalAirportRestTime();
						}
						
					}
				}
			}
		}
		
		
		DateTimeRange dtr = new DateTimeRange(startTime,endTime);
		
		return dtr.getDuration()+LinkLineConstant.DUTY_DURATION_ADD-arrivalAirportRestTimeSum;
	}
	
	/**
	 * 查询某一个人 一天的执勤期
	 * 计算执勤期的核心计算公式！
	 * 规则：
	 * 1.获取所有连线的第一个航班STD作为开始时间，如果没有STD，则取第一个连线的TD  -->StartTime
	 * 2.获取最后一个航班的innTimeAcars滑入时间作为结束时间，如果没有滑入，则取innTimeQar，如果没有innTimeQar，则取sta，作为结束时间   -->EndTime
	 * 历史飞行执勤期(B)=最后一个航段的滑入-报到时间（第一个航段的计飞-90分钟）-过站休息时间
	 * 所有飞行执勤期（包括未来） EndTime - StartTime + 90分钟 - 所有航班的落地过站休息时间
	 * 
	 * @param flightInfoList 是某个任务环某一天的所有连线，必须正序排序
	 * @return
	 */
	public static int getDutyDurationByFlightInfo(String dutyCode,List<FlightInfo> flightInfoList){
		if(CollectionUtils.isEmpty(flightInfoList)){
			return 0;
		}
		FlightInfo fristFlightInfo = flightInfoList.get(0);
		FlightInfo lastFlightInfo = flightInfoList.get(flightInfoList.size()-1);
		
		Date startTime = fristFlightInfo.getStd();
	
		
		Date endTime = lastFlightInfo.getSta();

		if(lastFlightInfo!=null){
			if(lastFlightInfo.getInnTimeAcars() != null){
				endTime = lastFlightInfo.getInnTimeAcars() ;
			}else if(lastFlightInfo.getInnTimeQar()!=null){
				endTime = lastFlightInfo.getInnTimeQar();
			}else if(lastFlightInfo.getSta() != null){
				endTime = lastFlightInfo.getSta();
			}
		}
					
		//过站休息期计算
		int arrivalAirportRestTimeSum = 0;
			for(FlightInfo f : flightInfoList){
				for(TaskLoopFlightFact t : f.getTaskLoopFlightFactList()){
					if(StringUtils.equals(t.getModuleFlag(), dutyCode)){
						if(t.getArrivalAirportRestTime() !=null){
							arrivalAirportRestTimeSum += t.getArrivalAirportRestTime();
						}
						
					}
				}
			}

		DateTimeRange dtr = new DateTimeRange(startTime,endTime);
		return dtr.getDuration()+LinkLineConstant.DUTY_DURATION_ADD-arrivalAirportRestTimeSum;
	}
	
	
	
	/**
	 * 
	 * @param linkLines 某一个人一天的所有连线
	 * @return
	 */
	public static int getDutyDurationByLinkLineBak(List<LinkLine> linkLines){
		if(CollectionUtils.isEmpty(linkLines)){
			return 0;
		}
		List<Date> tdtaList = new ArrayList<>();
		for(LinkLine l : linkLines){
			tdtaList.add(l.getTd());
			tdtaList.add(l.getTa());
		}
		Collections.sort(tdtaList);  
		
		Date start = tdtaList.get(0);
		Date end = tdtaList.get(tdtaList.size()-1);
		DateTimeRange dtr = new DateTimeRange(start,end);
		
		return dtr.getDuration()+LinkLineConstant.DUTY_DURATION_ADD;
	}
	
	/**
	 * 检查每个飞行员每天的执勤期是否符合局方规定
	 * @param list
	 * @param crew
	 * @return
	 */
	public static boolean dutyDurationCkEveryDay(List<CrewPositionAssignment> list,Crew crew){
		List<ValueForDate> valueForDateList = new ArrayList<>();
		List<LinkLine> linkLineList = new ArrayList<>(CrewPositionAssignmentUtil.getLinkLineSet(list));
		ListSortUtil.sort(linkLineList, true, "ta");
		Map<Date,List<LinkLine>> dateLinkLineMap = linkLineGroupByFlightDate(linkLineList);
		for(Map.Entry<Date,List<LinkLine>> entry: dateLinkLineMap.entrySet()) {
			ValueForDate plan = new ValueForDate(entry.getKey(),flightDutyDurationSum(entry.getValue()));
			valueForDateList.add(plan);
		}
		
		for(ValueForDate vd : valueForDateList){
			int durationEveryDay = vd.getValue();//每天的执勤期
			FlightInfo fristFlightInfo = getFristFlightInfo(vd.getDate(), linkLineList);
			int flightCount = getFlightInfoCount(vd.getDate(), linkLineList);
			
			if(!dutyDurationCkEveryDayCore(durationEveryDay, fristFlightInfo, flightCount)){
				return false;
			}
		}
		return true;
	}
	
	
	
	private static  FlightInfo getFristFlightInfo(Date flightDate,List<LinkLine> linkLineList){
		for(LinkLine l : linkLineList){
			if(flightDate.equals(l.getFlightDate())){
				if(CollectionUtils.isNotEmpty(l.getFlightInfoList())){
					return l.getFlightInfoList().get(0);
				}
			}
		}
		return null;
	}
	
	private static  int getFlightInfoCount(Date flightDate,List<LinkLine> linkLineList){
		int count = 0;
		for(LinkLine l : linkLineList){
			if(flightDate.equals(l.getFlightDate())){
				count += l.getFlightInfoList().size();
			}
		}
		return count;
	}
	
	/**
	 * 判断每天的执勤期的核心算法
	 *  1-4个航段报到时间在00:00-05:59，最大飞行值勤期12小时
		1-4个航段报到时间在06:00-11:59，最大飞行值勤期14小时
		1-4个航段报到时间在12:00-23:59，最大飞行值勤期13小时
		5个航段报到时间在00:00-05:59，最大飞行值勤期11小时
		5个航段报到时间在06:00-11:59，最大飞行值勤期13小时
		5个航段报到时间在12:00-23:59，最大飞行值勤期12小时
		6个航段报到时间在00:00-05:59，最大飞行值勤期10小时
		6个航段报到时间在06:00-11:59，最大飞行值勤期12小时
		6个航段报到时间在12:00-23:59，最大飞行值勤期11小时
		7个航段报到时间在00:00-05:59，最大飞行值勤期9小时
		7个航段报到时间在06:00-11:59，最大飞行值勤期11小时
		7个航段报到时间在12:00-23:59，最大飞行值勤期10小时

	 * @param durationEveryDay
	 * @param fristFlightInfo
	 * @param flightCount
	 * @return
	 */
	public static boolean dutyDurationCkEveryDayCore(int durationEveryDay,FlightInfo fristFlightInfo,int flightCount){
		if(fristFlightInfo == null){
			return true;
		}
		DateTime start = new DateTime(fristFlightInfo.getStd()).minusMinutes(LinkLineConstant.DUTY_DURATION_ADD_START);//报到时间
		int hourOfDay = start.getHourOfDay();
		if(flightCount<=4 && hourOfDay>=0 && hourOfDay<=5 && durationEveryDay <=12*60){
			return true;
		}else if(flightCount<=4 && hourOfDay>=6 && hourOfDay<=11 && durationEveryDay <=14*60){
			return true;
		}else if(flightCount<=4 && hourOfDay>=12 && hourOfDay<=23 && durationEveryDay <=13*60){
			return true;
		}else if(flightCount==5 && hourOfDay>=0 && hourOfDay<=5 && durationEveryDay <=11*60){
			return true;
		}else if(flightCount==5 && hourOfDay>=6 && hourOfDay<=11 && durationEveryDay <=13*60){
			return true;
		}else if(flightCount==5 && hourOfDay>=12 && hourOfDay<=23 && durationEveryDay <=12*60){
			return true;
		}else if(flightCount==6 && hourOfDay>=0 && hourOfDay<=5 && durationEveryDay <=10*60){
			return true;
		}else if(flightCount==6 && hourOfDay>=6 && hourOfDay<=11 && durationEveryDay <=12*60){
			return true;
		}else if(flightCount==6 && hourOfDay>=12 && hourOfDay<=23 && durationEveryDay <=11*60){
			return true;
		}else if(flightCount==7 && hourOfDay>=0 && hourOfDay<=5 && durationEveryDay <=9*60){
			return true;
		}else if(flightCount==7 && hourOfDay>=6 && hourOfDay<=11 && durationEveryDay <=11*60){
			return true;
		}else if(flightCount==7 && hourOfDay>=12 && hourOfDay<=23 && durationEveryDay <=10*60){
			return true;
		}else{
			return false;
		}
	}
	
	

}
